package sg.edu.nus.comp.cs4218.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import sg.edu.nus.comp.cs4218.impl.ATool;
import sg.edu.nus.comp.cs4218.extended2.ICommTool;

public class CommTool extends ATool implements ICommTool {
	public String currentLine1 = "";
	public String currentLine2 = "";

	/**
	 * Constructor
	 * 
	 * @param args
	 *            - Arguments the tool is going to be executed with.
	 */
	public CommTool(String[] args) {
		super(args);
	}

	/**
	 * Compare input1 and input2.
	 * 
	 * @param input1
	 *           - is a line from the first file
	 * @param input2
	 *           - is a line from the second file
	 * @return Return 0 when both equals. Return < 0 if input1 is
	 *         lexicographically less than input2. Return > 0 if input1 is
	 *         lexicographically more than input2.
	 */
	@Override
	public String compareFiles(String input1, String input2) {
		return String.valueOf(input1.compareTo(input2));
	}

	/**
	 * First, check that input1 and currentLine1 is sorted in increasing order.
	 * Second, check that input2 and currentLine2 is sorted in increasing order. 
	 * Third, compare input1 and input2.
	 * 
	 * @param input1
	 *           - is a line from the first file
	 * @param input2
	 *           - is a line from the second file
	 * @return Return 0 when both equals. Return < 0 if input1 is
	 *         lexicographically less than input2. Return > 0 if input1 is
	 *         lexicographically more than input2.
	 */
	@Override
	public String compareFilesCheckSortStatus(String input1, String input2) {
		boolean isSorted;

		if (currentLine1.compareTo(input1) > 0) {
			isSorted = false;
		} else if (currentLine2.compareTo(input2) > 0) {
			isSorted = false;
		} else {
			isSorted = true;
		}

		if (isSorted) {
			setStatusCode(0);
			return compareFiles(input1, input2);
		} else {
			setStatusCode(1);
			return "Not Sorted!";
		}
	}

	/**
	 * Compare input1 and input2.
	 * 
	 * @param input1
	 *           - is a line from the first file
	 * @param input2
	 *           - is a line from the second file
	 *           
	 * @return Return 0 when both equals. Return < 0 if input1 is
	 *         lexicographically less than input2. Return > 0 if input1 is
	 *         lexicographically more than input2.
	 */
	@Override
	public String compareFilesDoNotCheckSortStatus(String input1, String input2) {
		return compareFiles(input1, input2);
	}

	/**
	 * Display help for comm command.
	 * 
	 * @return Options available for comm command
	 */
	@Override
	public String getHelp() {
		return "comm : Compares two sorted files line by line. With no options, produce three-column output."
				+ "\nColumn one contains lines unique to FILE1, column two contains lines unique to FILE2,"
				+ "\nand column three contains lines common to both files. "
				+ "\nNOTE: two lines are consider the common to both files if both lines have the same characters "
				+ "and both line number are the same"
				+ "\n"
				+ "\nCommand Format - comm [OPTIONS] FILE1 FILE2"
				+ "\nFILE1 - Name of the file 1"
				+ "\nFILE2 - Name of the file 2"
				+ "\n-c : check seperately that the FILE1 and FILE2 is correctly sorted (increasing order). "
				+ "\n if it is not correctly sorted, 'Not Sorted!' is shown"
				+ "\n-d : do not check that the FILE1 and FILE2 are correctly sorted"
				+ "\n-help : Brief information about supported options";
	}

	/**
	 * First, checks whether commands, options and fileName in args, stdin is valid.
	 * Second,  call appropriate methods according to options.
	 * 
	 * @param workingDir
	 *            - current working directory
	 * @param stdin
	 *            - user input content (to be used when file is not given)
	 *            
	 * @return Output generated by executing Comm command
	 */
	@Override
	public String execute(File workingDir, String stdin) {
		currentLine1 = "";
		currentLine2 = "";
		String fileName1 = "";
		String fileName2 = "";
		String result = "";

		if (args == null) {
			setStatusCode(1);
			return "Invalid command";
		}
		// one pass to find -help
		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-help")) {
				setStatusCode(0);
				return getHelp();
			}
		}
		setStatusCode(0);
		// check whether command is valid
		if (args.length == 2) {
			// must be fileName1 fileName2
			fileName1 = args[0];
			if(!checkFileValidty(workingDir, fileName1)){
				setStatusCode(1);
			}
			fileName2 = args[1];
			if(!checkFileValidty(workingDir, fileName2)){
				setStatusCode(1);
			}

			result = executeNoOptionAndOptionD(workingDir, fileName1, fileName2);
		} else if (args.length == 3) {
			// must be -c fileName1 fileName2 
			// OR -d fileName1 fileName2
			if (args[0].equals("-c") || args[0].equals("-d")) {
				fileName1 = args[1];
				if(!checkFileValidty(workingDir, fileName1)){
					setStatusCode(1);
				}
				fileName2 = args[2];
				if(!checkFileValidty(workingDir, fileName2)){
					setStatusCode(1);
				}
								
				if (args[0].equals("-c"))
					result = executeOptionC(workingDir, fileName1, fileName2);
				else 
					result = executeNoOptionAndOptionD(workingDir, fileName1, fileName2);
			} else 
				setStatusCode(1);
		} else 
			setStatusCode(1);
		if(getStatusCode() == 1)
			return "Invalid command";
		else
			return result;
	}
	
	// helper methods
	
	/**
	 * For executing no option and -d option.
	 * Compares both files line by line.
	 * 
	 * @param workingDir
	 *            - current working directory
	 * @param fileName1
	 *            - first file name (file residing in workingDir)
	 * @param fileName2
	 *            - second file name (file residing in workingDir)
	 *            
	 * @return Returns 3 columns, first column are lines unique to fileName1, 
	 * second column are lines unique to fileName2,
	 * third column are lines common to both files (both line positions/numbers are the same & all characters are the same).
	 * Note: When 2 lines are different, the lexicographically smaller line is printed first follow by lexicographically larger line.
	 *
	 */
	private String executeNoOptionAndOptionD(File workingDir, String fileName1, String fileName2){
		// open file
		BufferedReader br1 = null;
		BufferedReader br2 = null;
		String line1 = "";
		String line2 = "";
		String result = "";
		try {
			br1 = new BufferedReader(new FileReader(workingDir
					+ File.separator + fileName1));
			br2 = new BufferedReader(new FileReader(workingDir
					+ File.separator + fileName2));
			// compareFiles
			while ((line1 = br1.readLine()) != null) {
				if ((line2 = br2.readLine()) != null) {
					int comparedNum = Integer
							.parseInt(compareFiles(line1, line2));
					if (result.length() > 0)
						result += "\n";
					
					if (comparedNum < 0) {		
						result += line1 + "\n\t\t" + line2;
					} else if (comparedNum > 0) {
						result += "\t\t" + line2 + "\n" + line1;
					} else 
						result += "\t\t\t\t" + line1;
				} else {
					// if file1 still has line
					if (result.length() > 0) 
						result += "\n";				
					result += line1;
				}
			}
			// if file2 still has line
			while ((line2 = br2.readLine()) != null) {
				if (result.length() > 0) 
					result += "\n";
				result += "\t\t" + line2;
			}
		} catch (IOException e) {
			setStatusCode(1);
			return "";
		} finally {
			try {
				if (br1 != null)
					br1.close();
				if (br2 != null)
					br2.close();
			} catch (IOException ex) {
				try{
					if (br2 != null)
						br2.close();
				} catch(IOException ex1){
				}
			}
		}
		setStatusCode(0);
		return result;
	}
	
	/**
	 * For executing -c option.
	 * Compares both files line by line and that the lines are sorted in each individual file.
	 * Comparation stops when a file is found to be not sorted (i.e not in increasing order).
	 * 
	 * @param workingDir
	 *            - current working directory
	 * @param fileName1
	 *            - first file name (file residing in workingDir)
	 * @param fileName2
	 *            - second file name (file residing in workingDir)
	 *            
	 * @return Returns 3 columns, first column are lines unique to fileName1, 
	 * second column are lines unique to fileName2,
	 * third column are lines common to both files (both line positions/numbers are the same & all characters are the same).
	 * The 3 columns returned are sorted in increasing order.
	 * If it is not sorted, "Not Sorted!" is concentrated behind the 3 columns result.
	 */
	private String executeOptionC(File workingDir, String fileName1, String fileName2){
		BufferedReader br1 = null;
		BufferedReader br2 = null;
		String result = "";
		// open files
		try {
			br1 = new BufferedReader(new FileReader(
					workingDir + File.separator + fileName1));
			br2 = new BufferedReader(new FileReader(
					workingDir + File.separator + fileName2));

			result = executeOptionCCompareFiles(br1, br2);
			
		} catch (IOException e) {
			setStatusCode(1);
			return "";
		} finally {
			try {
				if (br1 != null)
					br1.close();
				if (br2 != null)
					br2.close();
			} catch (IOException ex) {
				try{
					if (br2 != null)
						br2.close();
				} catch(IOException ex1){
				}
			}
		}
		setStatusCode(0);
		return result;
	}

	/**
	 * For executing -c option.
	 * Compares both files line by line and that the lines are sorted in each individual file.
	 * Comparation stops when a file is found to be not sorted (i.e not in increasing order).
	 * 
	 * @param br1
	 *            - for reading the first file
	 * @param br2
	 *            - for reading the second file
	 *            
	 * @return Returns 3 columns, first column are lines unique to fileName1, 
	 * second column are lines unique to fileName2,
	 * third column are lines common to both files (both line positions/numbers are the same & all characters are the same).
	 * The 3 columns returned are sorted in increasing order.
	 * If it is not sorted, "Not Sorted!" is concentrated behind the 3 columns result.
	 */
	private String executeOptionCCompareFiles(BufferedReader br1,
			BufferedReader br2) throws IOException {
		String result = "";
		String line1;
		String line2;
		while ((line1 = br1.readLine()) != null) {
			if (result.length() > 0)
				result += "\n";
			if ((line2 = br2.readLine()) != null) {
				String temp = compareFilesCheckSortStatus(
						line1, line2);
				if (temp.equals("Not Sorted!")) {				
					setStatusCode(0);
					return result += "Not Sorted!";
				} else {
					int comparedNum = Integer.parseInt(temp);
					if (comparedNum < 0) {
						result += line1 + "\n\t\t"
								+ line2;
					} else if (comparedNum > 0) {
						result += "\t\t" + line2 + "\n"
								+ line1;
					} else 
						result += "\t\t\t\t" + line1;
				}
				currentLine2 = line2;
			} else {
				// if file1 still has line, compare with its previous line
				int comparedNum = Integer
						.parseInt(compareFiles(currentLine1, line1));
				if (comparedNum <= 0) {					
					result += line1;
				} else {
					setStatusCode(0);
					return result += "Not Sorted!";
				}
			}
			currentLine1 = line1;
		}
		// if file2 still has line
		while ((line2 = br2.readLine()) != null) {
			// compare with its previous line
			int comparedNum = Integer
					.parseInt(compareFiles(currentLine2, line2));
			if (result.length() > 0)
				result += "\n";
			if (comparedNum <= 0) {
				result += "\t\t" + line2;
			} else  {
				setStatusCode(0);
				return result += "Not Sorted!";
			}
		}
		return result;
	}

	/**
	 * Check whether file is valid (not a directory and file exists)
	 * 
	 * @param workingDir
	 *            - current working directory
	 * @param fileName
	 *            - file name to be checked whether it resides in workingDir
	 *            
	 * @return returns false if file does not exists or it is a directory, else true
	*/
	private boolean checkFileValidty(File workingDir, String fileName) {
		File f = new File(workingDir + File.separator + fileName);
		if (!f.exists() || f.isDirectory())
			return false;
		
		return true;
	}
}
